package com.oop.example.work.bel;

import com.oop.example.utils.Utils;
import com.oop.example.processing.ParseError;
import com.oop.example.processing.PrepareError;
import com.oop.example.processing.Parser;
import com.oop.example.data.EntryPOJO;
import com.oop.example.xml.Block;
import com.oop.example.xml.Entry;
import com.oop.example.xml.Entry.Heading;
import com.oop.example.xml.Entry.Heading.Vocable;
import com.oop.example.xml.Entry.Heading.Vocable.Part;
import com.oop.example.xml.ObjectFactory;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.*;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class EntryHandler implements Comparable<EntryHandler> {

    private ObjectFactory factory = new ObjectFactory();
    private Properties labelDictionary = Utils.getLabelDictionary();
    private EntryPOJO entryPojo;
    private String header = "";
    private String[] headerss = {};

    public String[] getHeaders() {
        return headerss;
    }
    private String body = "";
    private Entry entry;
    private List<Entry> childEntry = null;
    private boolean checkItalic = false;

    public EntryHandler(String fromParser, String headerOfParent) {

        fromParser = fromParser.trim();
        int indexOf = fromParser.indexOf("]");
        this.header = fromParser.substring(1, indexOf).trim();
        header = header.replaceAll(",@", "COMA");
        header = new HeaderFormatter(headerOfParent, header).getFormatted();
        //header = header.replaceAll("COMA", ",@");
        this.body = fromParser.substring(indexOf + 1).trim();
        entryPojo = new EntryPOJO();
        entry = factory.createEntry();
        //System.out.println("header:" + header + "; body:" + body);
    }

    public boolean isCheckItalic() {
        return checkItalic;
    }

    public void setCheckItalic(boolean checkItalic) {
        this.checkItalic = checkItalic;
    }

    public EntryPOJO getEntryPOJO() {
        return entryPojo;
    }

    public void setEntryPOJO(EntryPOJO entry) {
        this.entryPojo = entry;
    }

    public List<Entry> getChildEntrys() {
        if (childEntry == null) {
            childEntry = new ArrayList<Entry>();
        }
        return childEntry;
    }

    public Entry getEntry() {
        return entry;
    }
    private int rowID = 0;

    public int getRowID() {
        return rowID;
    }

    public void setRowID(int rowID) {
        this.rowID = rowID;
    }

    public EntryHandler() {
        entry = factory.createEntry();
    }

    public boolean hasHomonym() {
        for (String s : Utils.homonymNumeration) {
            if (header.trim().contains(s)) {
                //System.out.println("homonym : " + header);
                return true;
            }
        }
        return false;
    }

    /**
     * Является ли данная статья первым омонимом
     * @return 
     */
    //public boolean isFirstHomonym() {
    //    return header.trim().endsWith(Utils.homonymNumeration[Utils.homonymNumeration.length - 1]);
    //}
    public String getHeader() {
        return header;
    }

    public String getHomonymHeader() {
        String homonymHeader = header;
        for (String s : Utils.homonymNumeration) {
            if (header.trim().contains(s)) {
                homonymHeader = homonymHeader.replace(s, "");
                //break;
            }
        }
        return homonymHeader;
    }

    public String getBody() {
        return body;
    }

    public void handle() throws ParseError, PrepareError, NoSuchParserException {
        //Извлечение заголовка и тела из статьи
        extractBody();
        parseHeader(true);
        parseBody();
    }

    public void liteHandle() throws ParseError, PrepareError, NoSuchParserException {
        //Извлечение заголовка и тела из статьи
        //extractBody();
        parseHeader(true);
        parseBody();
    }

    public void veryLiteHandle() throws ParseError, PrepareError, NoSuchParserException {
        //Извлечение заголовка и тела из статьи
        //extractBody();
        extractBody();
        //parseHeader(true);
    }

    /**
    * Извлечение заголовка и тела из статьи
    */
    private void extractBody() throws PrepareError {
        String content = entryPojo.getContent();
        try {
            InputStream is = new ByteArrayInputStream(content.getBytes("UTF-8"));
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
            Document doc = dBuilder.parse(is);
            doc.getDocumentElement().normalize();
            Element root = doc.getDocumentElement();
            NodeList childNodes = root.getChildNodes();
            boolean onStart = true;
            int distanceAfterHeader = 0;
            for (int i = 0; i < childNodes.getLength(); i++) {
                Node item = childNodes.item(i);
                if (onStart
                        && item.getNodeType() == Node.ELEMENT_NODE
                        && item.getNodeName().equalsIgnoreCase("b")) {
                    header += item.getTextContent();
                } else if (onStart
                        && item.getNodeType() == Node.TEXT_NODE
                        && (item.getTextContent().equalsIgnoreCase(Utils.ACCENT_SYMBOL)
                        || item.getTextContent().equalsIgnoreCase("-")
                        || item.getTextContent().equalsIgnoreCase(",")
                        || item.getTextContent().equalsIgnoreCase(" "))) {
                    header += item.getTextContent();
                } else if (item.getTextContent().equalsIgnoreCase(Utils.ACCENT_SYMBOL)
                        && distanceAfterHeader == 0) {
                    distanceAfterHeader++;
                } else {
                    if (item.getNodeType() == Node.ELEMENT_NODE && item.getNodeName().equalsIgnoreCase("b")) {
                        body += "[" + item.getTextContent() + "]";
                    } else if (item.getNodeType() == Node.ELEMENT_NODE && item.getNodeName().equalsIgnoreCase("i")) {
                        String italic = item.getTextContent();
                        List<String> list = new ArrayList<String>();
                        for (String str : labelDictionary.stringPropertyNames()) {
                            list.add(str);
                        }
                        Collections.sort(list, new LengthComparator());
                        for (String s : list){
                            italic = italic.replace(s, "").trim();                                    
                        }                       
                        
                        if (italic.startsWith("(") && italic.endsWith(")")) {
                            //все ок - это комментарий
                        } else if (italic.startsWith("&") && italic.endsWith("&")) {
                        } else if (italic.isEmpty()) {
                            //все ок - это помета
                            //} else if (){
                        } else {
                            if (checkItalic) {
                                throw new RuntimeException("Курсив, там где ему не положено быть : " + italic);
                            }
                        }
                        body += item.getTextContent();
                    } else {
                        body += item.getTextContent();
                    }
                    distanceAfterHeader++;
                    onStart = false;
                }
            }
            body = fixBody(body);
            wrapRefs();
            body = wrapLabels(body, labelDictionary);
            if (!checkHeader(header, content)) {
                //System.out.println("Статья содердит ошибки");
            }
            //System.out.println("<" + header + "> " + body);
        } catch (Exception e) {
            e.printStackTrace(System.out);
            System.out.println("ERROR SRC: " + content);
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
        }
        String[] badChars = {",", ";", "!", ")", "?"};
        for (String bc : badChars) {
            if (body.startsWith(bc)) {
                throw new RuntimeException("Тело статьи начинается с недопустимого символа: \"" + bc + "\"");
            }
        }
    }
    private static final Map<String, String> map = new HashMap<String, String>();

    static {
        map.put("  ", " ");
        map.put("\\[ ", " [");
        map.put(" \\]", "] ");
        map.put("\\[;\\]", ";");
        map.put("\\[%\\]", "%");
        map.put("\\[\\.\\]", ".");
        map.put("\\[\\)\\]", ")");
        map.put("\\[\\(\\]", "(");
        map.put("\\[\\]", "");
        map.put(",%", ",");
        map.put("\\[;", ";[");
        map.put("\\]\\.", ".]");
        map.put("\\] \\[", " ");
        map.put(" \\,", ",");
        map.put("'", "’");
        //map.put(" \\&", " ");
        map.put("в разн знач\\.", "в разн. знач.");
        map.put("в разн\\.знач\\.", "в разн. знач.");
        map.put("в знач сказ\\.", "в знач. сказ.");
        map.put("уменьш\\-ласк\\.", "уменьш\\.\\-ласк\\.");
        map.put("\\^\\(", "^|");
        map.put("\\)\\^", "|^");
        map.put("\\] \\~\\[", " ~");
        map.put("\\[\\,\\@\\]", ",@");
        map.put("\\[\\,\\]", ",");
        map.put("\\[\\@\\]", "@");
        map.put("\\& ", " &; ");
        map.put("\\& ", " &; ");
        map.put("\\]\\-\\[", "-");
        map.put("\\]\\ - \\[", " - ");
        map.put("\\]\\- \\[", "-");
        map.put("\\]\\ -\\[", "-");

        map.put("\\]\\ – \\[", " – ");
        map.put("\\]\\–\\[", "–");
        map.put("\\]\\– \\[", "–");
        map.put("\\]\\ –\\[", "–");
    }

    private String fixBody(String bdy) {

        bdy = bdy.replaceAll("◊", "ROMB");
        bdy = bdy.replaceAll("\\*", "* ");
        bdy = bdy.replaceAll("\\@", "@ ");
        //bdy = bdy.replaceAll(" \\&", " & ");
        //bdy = bdy.replaceAll("\\.", ". ");

        boolean exit = false;
        while (!exit) {
            exit = true;
            for (String key : map.keySet()) {
                String cont = key.replace("\\", "");
                if (bdy.contains(cont)) {
                    bdy = bdy.replaceAll(key, map.get(key));
                    exit = false;
                    break;
                }
            }
        }
        String dog = "@[\u0400-\u045F]";
        String[] split = bdy.split(dog);
        if (split.length > 1) {
            for (int i = 0; i < split.length; i++) {
                dog += split[i] + "@";
            }
        }
        if (!bdy.endsWith(";")) {
            bdy += ";";
        }
        return bdy.trim();
    }

    private void wrapRefs() {
        body = body.replaceAll(" см\\. ", " {см.} ");
    }

    public static String wrapLabels(String body, Properties labelDictionary) {
        String[] labelChars = {" ", ",", ";", ":", "&", "(", ")", "$"};
        //String some = " |\\,|\\;|\\.|\\:";
        //Сортировка ключей по длине - сначала самые длинные потом короткие
        List<String> list = new ArrayList<String>();
        for (String str : labelDictionary.stringPropertyNames()) {
            list.add(str);
        }
        Collections.sort(list, new LengthComparator());
        for (String key : list) {
            for (String start : labelChars) {
                for (String end : labelChars) {
                    if (body.startsWith(key + end)) {
                        body = body.replace(key + end, "<" + key + ">" + end);
                    }
                    if (body.endsWith(start + key)) {
                        body = body.replace(start + key, start + "<" + key + ">");
                    }
                    while (body.contains(start + key + end)) {
                        body = body.replace(start + key + end, start + "<" + key + ">" + end);
                    }
                }
            }
        }
        body = body.replaceAll("\\<\\<мн\\.\\> нет\\>", "<мн. нет>");
        return body;
    }

    private boolean checkHeader(String header, String source) {
        boolean hasError = false;
        String[] denyChars = {"(", ")", "/", ";", ":", "&"};
        for (String str : denyChars) {
            if (header.contains(str)) {
                throw new RuntimeException("ERROR Line " + rowID + ": Символ '" + str + "' в заголовке статьи " + getText(source));
            }
        }
        if (header.isEmpty()) {
            throw new RuntimeException("ERROR Line " + rowID + ": Пустой заголовок статьи : " + source);
        }
        return !hasError;
    }

    private String getText(String htmlSource) {
        String result = htmlSource;
        result = htmlSource.replaceAll("<p>", "").replaceAll("</p>", "").replaceAll("<i>", "").replaceAll("</i>", "").replaceAll("<b>", "").replaceAll("</b>", "").replaceAll("&#769;", "́");
        return result;
    }

    private void parseBody() throws ParseError, NoSuchParserException {
        if (body.contains("^%") || body.contains("^!")) {
            throw new RuntimeException("Символы: ^% или ^! ");
        }
        Parser parser = ParserFactory.getParser(this);
        parser.parse();
        //String entryTypeMask = parser.getEntryType();
        //entryPojo.setEntryType(entryTypeMask);
    }

    private void parseHeader(boolean split) {
        List<String> headers = new ArrayList<String>();
        if (split) {
            headers.addAll(Arrays.asList(header.split(",")));
        } else {
            headers.add(header);
        }
        header = header.replaceAll("COMA", ",");
        if (headers.size() > 1) {
            header = headers.get(0);
        }
        headerss = headers.toArray(new String[]{});
        Heading entryHeading = factory.createEntryHeading();
        boolean isPrimary = true;
        for (String hdr : headers) {
            hdr = hdr.trim().replaceAll("COMA", ",");
            if (hdr.isEmpty()) {
                throw new RuntimeException("Пустой заголовок статьи");
            }
            if (hdr.contains("^%") || hdr.contains("^!")) {
                throw new RuntimeException("Символы: ^% или ^! ");
            }
            if (hdr.matches(".*\\(\\~.*\\).*")) {
                throw new RuntimeException("Скобки с тильдой в заголовке");
            }
            //Удаляем римские цифры из хвоста
            for (String rom : Utils.homonymNumeration) {
                hdr = hdr.replace(rom, "");
            }
            Vocable entryHeadingVocable = factory.createEntryHeadingVocable();
            if (headers.size() > 1) {
                entryHeadingVocable.setIsPrimary(isPrimary);
            }
            Part entryHeadingVocablePart = factory.createEntryHeadingVocablePart();
            Block block = new Block();
            hdr = handleAccentSymbol(block, hdr);
            block.setValue(hdr);
            JAXBElement<Block> simple = factory.createEntryHeadingVocablePartSimple(block);
            entryHeadingVocablePart.getPartChildren().add(simple);
            entryHeadingVocable.getPart().add(entryHeadingVocablePart);
            entryHeading.getVocable().add(entryHeadingVocable);
            isPrimary = false;
        }
        entry.setHeading(entryHeading);
    }

    public static String handleAccentSymbol(Block block, String word) {
        List<Integer> symbols = new ArrayList<Integer>();

        String indexEs = "";
        while (word.contains(Utils.ACCENT_SYMBOL)) {
            int indexOfAccent = word.indexOf(Utils.ACCENT_SYMBOL) - 1;
            word = word.replaceFirst(Utils.ACCENT_SYMBOL, "");
            if (!symbols.contains(indexOfAccent)) {
                symbols.add(indexOfAccent);
                indexEs += indexOfAccent + ";";
            }
        }
        if (indexEs.contains("-")) {
            throw new RuntimeException("Минус в accentSymbolNumber");
        }
        if (!indexEs.isEmpty()) {
            indexEs = indexEs.substring(0, indexEs.length() - 1);
            block.setAccentSymbolNumber(indexEs);
        }
        //if ()
        return word;
    }

    @Override
    public String toString() {
        try {
            StringWriter st = new StringWriter();
            JAXBContext ctx = JAXBContext.newInstance(Entry.class);
            Marshaller marshaller = ctx.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
            marshaller.marshal(entry, st);
            String result = st.toString();
            if (childEntry != null && !childEntry.isEmpty()) {
                for (Entry child : childEntry) {
                    st = new StringWriter();
                    marshaller.marshal(child, st);
                    result += Utils.NL + st.toString();
                }
            }
            return result;
        } catch (Exception ex) {
            ex.printStackTrace(System.out);
        }
        return "";
    }

    @Override
    public int compareTo(EntryHandler o) {
        return this.getHeader().compareTo(o.getHeader());
    }

    public static class LengthComparator implements java.util.Comparator<String> {

        @Override
        public int compare(String s1, String s2) {
            return s2.length() - s1.length();
        }
    }
}
